package com.yvon.boot.designpattern.strategy;

import cn.hutool.extra.spring.SpringUtil;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 策略选择器
 *
 * @author yvon
 * @since  2023-07-27
 */
public class AbstractStrategyChoose implements ApplicationListener<ApplicationEvent> {

	/**
	 * 执行策略集合
	 */
	private final Map<String, AbstractStrategy> abstractStrategyMap = new HashMap<>();

	/**
	 * 根据 mark 查询具体策略
	 *
	 * @param mark 策略标识
	 * @return 实际执行策略
	 */
	public AbstractStrategy choose(String mark) {
		return Optional.ofNullable(abstractStrategyMap.get(mark)).orElseThrow(() -> new RuntimeException(String.format("[%s] 策略未定义", mark)));
	}

	/**
	 * 根据 mark 查询具体策略并执行
	 *
	 * @param mark         策略标识
	 * @param requestParam 执行策略入参
	 * @param <P>    执行策略入参范型
	 */
	public <P> void chooseAndExecute(String mark, P requestParam) {
		AbstractStrategy executeStrategy = choose(mark);
		executeStrategy.execute(requestParam);
	}

	/**
	 * 根据 mark 查询具体策略并执行，带返回结果
	 *
	 * @param mark         策略标识
	 * @param requestParam 执行策略入参
	 * @param <P>    执行策略入参范型
	 * @param <R>   执行策略出参范型
	 * @return
	 */
	public <P, R> R chooseAndExecuteResp(String mark, P requestParam) {
		AbstractStrategy executeStrategy = choose(mark);
		return (R) executeStrategy.executeResp(requestParam);
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		Map<String, AbstractStrategy> actual = SpringUtil.getBeansOfType(AbstractStrategy.class);
		actual.forEach((beanName, bean) -> {
			AbstractStrategy beanExist = abstractStrategyMap.get(bean.mark());
			if (beanExist != null) {
				throw new RuntimeException(String.format("[%s] Duplicate execution policy", bean.mark()));
			}
			abstractStrategyMap.put(bean.mark(), bean);
		});
	}
}
